<?php
/**
 * API親コントローラー
 *
 * @package  app
 * @extends  Controller
 */
require_once __DIR__ . '/../../thirdpirty/Fluent/Autoloader.php';
use Fluent\Logger\FluentLogger;

class Controller_Apibase extends Controller {
	/*
	 * 認証有無
	 */
	public $auth = true;

	/*
	 * ログ記録有無
	 */
	public $log = true;

	/*
	 * パス保存用
	 */
	public $paths = array();

	/*
	 * 会員情報
	 */
	public $member_result;

	/*
	 * 検索情報
	 */
	public $member_where_info;
	public $member_where_other;
	public $song_where;

	/**
	 * 前処理
	 *
	 * @access  public
	 * @return  Response
	 */
	public function before() {
		// FuelPHPのバグ対策
		// 'log_threshold' > Fuel::L_WARNING の時 Class 'Log' not found となる
		! class_exists('Log') and \Package::load('log');
		
		parent::before();
		$post = Input::post();
		$this -> member_result = null;

		// 楽曲関係
		if (!empty($post["song_title"]) ) {
			$s = array();
			$s['song.song_title'] = mb_substr($post['song_title'], 0, Config::get('maxlength_song'));
			$s['song.artist'] = isset($post['artist']) ? mb_substr($post['artist'], 0, Config::get('maxlength_artist')) : '';
			$this -> song_where = $s;
		}

		// 会員関係
		if (!empty($post["member_id"])) {
			$member_id = $post["member_id"];
			$this -> member_where_other = array('member_id' => $member_id);
		}

		if (!$this -> auth) {
			// 認証不要

		} else if (substr(Input::uri(), 0, 15) == 'api/member/auth') {
			// 新規
			if (empty($post["uuid"])) {
				throw new HttpNotFoundException;
			}
			if (strcmp($post["password"], Config::get('member_password')) != 0) {
				$this -> alert(sprintf("auth error %s password=%s", $member_id, $post["password"]));
				throw new HttpNotFoundException;
			}
		} else if (!empty($post["member_id"]) && !empty($post["access_token"])) {
			// access_token
			// 会員情報取得
			$this -> member_where_info = array('member_status' => 0, 'member_id' => $member_id, 'access_token' => $post["access_token"], );

			$query = DB::select('push_enable','point_has', 'sex', DB::expr("DATE_FORMAT(birth_date,'%Y/%m/%d') AS birth_date"), 'area');
			$query -> from('member_info');
			$query -> where($this -> member_where_info);
			$result = $query -> execute() -> as_array();

			if (count($result) != 1) {
				// 認証エラー
				$this -> alert(sprintf("auth error %s access_token=%s", $member_id, $post["access_token"]));
				throw new HttpNotFoundException;
			}
			$this -> member_result = $result[0];
		} else {
			$this -> alert('no token');
			throw new HttpNotFoundException;
		}
	}

	/**
	 * 後処理
	 *
	 * @access  public
	 * @return  Response
	 */
	public function after($response) {
		$response = parent::after($response);

		if ($this -> log) {
			$log = array();
			$log['uri'] = Input::uri();
			$log['status'] = $response -> status;
			$log = array_merge($log, Input::get());
			$log = array_merge($log, Input::post());
			// 不要なパラメーターを削除
			unset($log['/' . $log['uri']]);
			unset($log['password']);
			unset($log['identification']);
			unset($log['access_token']);
			$this -> accesslog($log);
		}

		return $response;
	}

	/**
	 * キャッシュキー作成
	 *
	 * @access  public
	 * @return  Response
	 */
	public function ckey($key1, $key2 = '', $key3 = '', $key4 = '', $key5 = '') {
		$ckey = sprintf("%s-%s-%s-%s-%s", $key1, $key2, $key3, $key4, $key5);
		$ckey = hash_hmac('sha256', $ckey, false);
		return $ckey;
	}

	/**
	 * 曲を取得
	 *
	 * @access  public
	 * @return  Response
	 */
	public function get_song($song_where) {
		$query = DB::select('song_id', 'song.song_title', 'song.artist', 'song.create_date', 'artwork', 'preview');
		$query -> from('song');
		$query -> where($song_where);
		$query -> join('itunes_song', 'LEFT') -> on('song.itunes_track_id', '=', 'itunes_song.itunes_track_id');
		$song = $query -> execute() -> as_array();
		if (count($song) == 0) {
			// 該当曲無し
			// 曲レコード作る
			Log::info("new song");

			$query = DB::insert('song') -> set($song_where);
			$query -> execute();
			$query = DB::select(DB::expr('last_insert_id() AS song_id'));
			$result = $query -> execute();
			$song = $result -> as_array();
		}

		return $song[0];
	}

	/**
	 * iTunesAPIから取得
	 *
	 * @access  public
	 * @return  Response
	 */
	public function get_itunesapi($word, $limit = 0) {
		$json = array();
		$limit = ($limit == 0) ? Config::get('maxrows_searchlist') : $limit;

		$url = sprintf("%s&limit=%d&term=%s", Config::get('itunes_url_api'), $limit, urlencode($word));
		try {
			$ckey = $this->ckey('itunes', $url);
			$json = Cache::get($ckey);
		} catch (\CacheNotFoundException $e) {
			try {
				$curl = Request::forge($url, 'curl');
				$result = $curl -> execute();
				$res = json_decode($result, true);
				// 上位ヒットの情報をsongに登録したいので逆順
				for ($i = $res["resultCount"] - 1; $i >= 0; $i--) {
					$entry = $res["results"][$i];

					if (!isset($entry['previewUrl'])) {
						// 試聴できない = 予約受付中
						continue;
					}
					if (!isset($entry['trackPrice']) || $entry['trackPrice'] <= 0) {
						// 価格がない = 予約受付中
						continue;
					}
					if (strcmp($entry['currency'], 'JPY') != 0) {
						// 価格が円じゃない = 存在するか不明
						continue;
					}
					if ($entry['trackPrice'] > Config::get('point_rate_max_itunes')) {
						// 高額楽曲
						continue;
					}

					$j = array();
					$j['song_title'] = $entry['trackCensoredName'];
					$j['artist'] = $entry['artistName'];
					$j['itunes_track_id'] = $entry['trackId'];
					$j['artwork'] = $entry['artworkUrl100'];
					$j['preview'] = $entry['previewUrl'];
					$j['link'] = $entry['trackViewUrl'];

					$itunes = $j;
					$itunes['album_title'] = $entry['collectionName'];
					$itunes['price'] = $entry['trackPrice'];
					$itunes['data'] = json_encode($entry);

					$json[] = $j;

					// 紐付け
					$this -> set_itunes2song($itunes);
				}

				$json = array_reverse($json); // 並びを元に戻す
				Cache::set($ckey, $json, Config::get('cache_itunes_expiration'));
			} catch (\Exception $e) {
				throw $e;
				$this -> alert($e);
			}
		}

		return $json;
	}

	/**
	 * iTunesRSSから取得
	 *
	 * @access  public
	 * @return  Response
	 */
	public function get_itunesrss($url = '') {
		$json = array();
		$url = ($url == '') ? Config::get('itunes_url_rss100') : $url;

		try {
			$ckey = $this->ckey('itunes', $url);
			$json = Cache::get($ckey);
		} catch (\CacheNotFoundException $e) {
			try {
				$curl = Request::forge($url, 'curl');
				$result = $curl -> execute();
				$res = json_decode($result, true);
				// 上位ヒットの情報をsongに登録したいので逆順
				for ($i = count($res['feed']['entry']) - 1; $i >= 0; $i--) {
					$entry = $res['feed']['entry'][$i];
					
					if (!isset($entry['link'][1])) {
						// 試聴できない = 予約受付中
						continue;
					}
					if (!isset($entry['im:price']['attributes']['amount']) || $entry['im:price']['attributes']['amount'] <= 0) {
						// 価格がない = 予約受付中
						continue;
					}
					if (strcmp($entry['im:price']['attributes']['currency'], 'JPY') != 0) {
						// 価格が円じゃない = 存在するか不明
						continue;
					}
					if ($entry['im:price']['attributes']['amount'] > Config::get('point_rate_max_itunes')) {
						// 高額楽曲
						continue;
					}

					$j = array();
					$j['song_title'] = $entry['im:name']['label'];
					$j['artist'] = $entry['im:artist']['label'];
					$j['itunes_track_id'] = $entry['id']['attributes']['im:id'];
					$j['artwork'] = $entry['im:image'][2]['label'];
					$j['preview'] = $entry['link'][1]['attributes']['href'];
					$j['link'] = $entry['id']['label'];

					$itunes = $j;
					$itunes['album_title'] = $entry['im:collection']['im:name']['label'];
					$itunes['price'] = $entry['im:price']['attributes']['amount'];
					$itunes['data'] = json_encode($entry);

					$json[] = $j;

					// 紐付け
					$this -> set_itunes2song($itunes);
				}

				$json = array_reverse($json); // 並びを元に戻す
				Cache::set($ckey, $json, Config::get('cache_itunes_expiration'));
			} catch (\Exception $e) {
				throw $e;
				$this -> alert($e);
			}
		}

		return $json;
	}

	/**
	 * iTunesと曲の紐付け
	 *
	 * @access  public
	 * @return  Response
	 */
	public function set_itunes2song($values) {
		// iTunes
		$query = DB::select(DB::expr('COUNT(*) AS count'));
		$query -> from('itunes_song');
		$query -> where('itunes_track_id', $values['itunes_track_id']);
		$result = $query -> execute() -> as_array();
		if( $result[0]['count'] == 0) {
			// 新規
			$query = DB::insert('itunes_song') -> set($values);
			$query -> execute();

		} else {
			// 更新
			$query = DB::update('itunes_song');
			$query -> set($values);
			$query -> where('itunes_track_id', $values['itunes_track_id']);
			$query -> execute();
		}

		// Song
		$v = array();
		$v['song_title'] = mb_substr($values['song_title'], 0, Config::get('maxlength_song'));
		$v['artist'] = mb_substr($values['artist'], 0, Config::get('maxlength_artist'));
		$v['itunes_track_id'] = $values['itunes_track_id'];
		$v['itunes_update_date'] = DB::expr('NOW()');
		
		$query = DB::select(DB::expr('COUNT(*) AS count'));
		$query -> from('song');
		$query -> where('song_title', $v['song_title']);
		$query -> where('artist', $v['artist']);
		$result = $query -> execute() -> as_array();
		if( $result[0]['count'] == 0) {
			// song
			$query = DB::insert('song') -> set($v);
			$query -> execute();
		} else {			
			// song
			$query = DB::update('song');
			$query -> set($v);
			$query -> where('song_title', $v['song_title']);
			$query -> where('artist', $v['artist']);
			$query -> execute();
		}
	}

	/**
	 * 成果取得
	 *
	 * @access  public
	 * @return  Response
	 */
	public function get_outcome() {
		$json = array();

		$outcomes = array('comment_post', 'twitter_follow', 'facebook_like', 'login_bonus');

		foreach ($outcomes as $key1) {
			// 有効性チェック
			$query = DB::select('member_id');
			$query -> from('member_point');
			$query -> where($this -> member_where_other);
			$query -> where('key1', $key1);
			switch( $key1 ) {
				case "comment_post" :
				case "login_bonus" :
					// １日１回
					$query -> where(DB::expr("DATE_FORMAT(create_date,'%Y-%m-%d')"), ">", DB::expr("DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 DAY),'%Y-%m-%d')"));
					break;
				case "twitter_follow" :
					break;
				case "facebook_like" :
					break;
				default :
					$this -> alert('outcome bad key1=' . $key1);
					return Response::forge(null, D_HTTP_WARNING);
					break;
			}
			$result = $query -> execute();

			// レスポンス
			if (count($result) == 0) {
				$json[$key1] = '0';
			} else {
				$json[$key1] = '1';
			}
		}

		// ログインボーナスの処理
		// 旧バージョンは除外する
		if( $json['login_bonus'] == '0' && $this->appver() != '1.0.2' && $this->appver() != '1.0.1' && $this->appver() != '1.0') {
			$this -> set_outcome($this -> member_where_other['member_id'], 'login_bonus');
		}

		return $json;
	}

	/**
	 * 成果付与
	 *
	 * @access  public
	 * @return  Response
	 */
	public function set_outcome($member_id, $key1, $key2 = '', $point = 0) {
		if( $point == 0) {
			if( $this->appver_more('1.0.3') ) {
				// ポイント調整の改修
				$points = array('comment_post' => 1, 'twitter_follow' => 5, 'facebook_like' => 5, 'login_bonus' => 1);
			} else {
				$points = array('comment_post' => 1, 'twitter_follow' => 10, 'facebook_like' => 10, 'login_bonus' => 2);
			}
			$point = $points[$key1];
		}

		// 登録開始
		DB::start_transaction();
		try {
			// ポイント履歴
			$query = DB::insert('member_point');
			$query -> set(array('member_id' => $member_id, 'key1' => $key1, 'key2' => $key2, 'point_value' => $point));
			$query -> execute();

			// ポイント付与
			$query = DB::update('member_info');
			$query -> set(array("point_has" => DB::expr("point_has+" . $point)));
			$query -> where($this -> member_where_info);
			$query -> execute();
			
			// 表示用
			if( isset($this -> member_result['point_has']) ) {
				$this -> member_result['point_has'] += $point;
			}
	
			// コミット
			DB::commit_transaction();
		} catch (\Exception $e) {
			throw $e;
			$this -> alert($e);

			// ロールバック
			DB::rollback_transaction();
			return false;
		}

		return true;
	}

	/**
	 * ランキング取得
	 *
	 * @access  public
	 * @return  Response
	 */
	public function get_ranking($kind, $period) {
		try {
			$ckey = $this -> ckey('ranking', 'get', $kind, $period);
			$json = Cache::get($ckey);
			Log::debug('Cache Hit');
		} catch (\CacheNotFoundException $e) {
			$table = '';
			$date = array();
			switch($period) {
				case '0' :
					// 24時間
					// 集計バッチのタイムラグを考え少し過去にしておく
					$t = time() - 60 * 60;
					$table = 'ranking_daily';
					$date[] = date("Y/m/d", $t);
					$date[] = date("Y/m/d", $t - 60 * 60 * 24);
					break;
				case '1' :
					// 週間
					$table = 'ranking_weekly';
					$date[] = date("Y/m/d", strtotime("Monday previous week"));
					$time = strtotime("Monday previous week");
					$date[] = date("Y/m/d", strtotime("Monday previous week", $time));
					break;
				case '2' :
					// 月間
					$table = 'ranking_monthly';
					$date[] = date('Y/m/',mktime(0,0,0,date('m')-1,1,date('Y'))) . "01";
					$date[] = date('Y/m/',mktime(0,0,0,date('m')-2,1,date('Y'))) . "01";
					break;
				default :
					break;
			}

			if (!empty($table)) {
				// 期間
				$res = array();
				foreach ($date as $key => $value) {
					if ($kind != 3) {
						// 曲DB
						$query = DB::select($table . '.song_id', 'song.song_title', 'song.artist', 'score', 'artwork', 'preview');
						$query -> from($table);
						$query -> where('ranking_kind', $kind);
						$query -> where('ranking_date', $value);
						$query -> join('song') -> on($table . '.song_id', '=', 'song.song_id');
						$query -> join('itunes_song', 'LEFT') -> on('song.itunes_track_id', '=', 'itunes_song.itunes_track_id');
					} else {
						// iTunes
						$query = DB::select(DB::expr('itunes_track_id AS song_id'), 'song_title', 'artist', 'score', 'artwork', 'preview', 'itunes_track_id', 'link');
						$query -> from($table);
						$query -> where('ranking_kind', $kind);
						$query -> where('ranking_date', $value);
						$query -> join('itunes_song') -> on($table . '.song_id', '=', 'itunes_song.itunes_track_id');
					}
					$query -> order_by("score", "desc");
					$query -> limit(Config::get('maxrows_rankinglist'));
					$res[$key] = $query -> bind('dt', $date_now) -> execute() -> as_array();
				}

				if (count($res[0]) == 0) {
					//結果がない場合は累計を表示
					$table = '';
				} else {
					$json = $res[0];
					$old = $res[1];
				}

			}
			if (empty($table)) {
				// 累計
				if ($kind != 3) {
					// 曲DB
					$count = array('count_comment', 'count_favorite', 'count_play');
					$query = DB::select('song_id', 'song.song_title', 'song.artist', DB::expr($count[$kind] . ' AS score'), 'artwork', 'preview');
					$query -> from('song');
					$query -> where($count[$kind], '>', '0');
					$query -> join('itunes_song', 'LEFT') -> on('song.itunes_track_id', '=', 'itunes_song.itunes_track_id');
					$query -> order_by($count[$kind], "desc");
					$query -> limit(Config::get('maxrows_rankinglist'));
					$json = $query -> execute() -> as_array();
				} else {
					// iTunes
					$query = DB::select(DB::expr('itunes_track_id AS song_id'), 'song_title', 'artist', DB::expr('score_ranking AS score'), 'artwork', 'preview', 'itunes_track_id', 'link');
					$query -> from('itunes_song');
					$query -> order_by('score_ranking', 'DESC');
					$query -> limit(Config::get('maxrows_rankinglist'));
					$json = $query -> execute() -> as_array();
				}

				// 過去との比較
				$query = DB::select('ranking_total.song_id', 'score');
				$query -> from('ranking_total');
				$query -> where('ranking_kind', $kind);
				$query -> where('ranking_date', date("Y/m/d", time() - 60 * 60 * 24));
				$query -> order_by("score", "desc");
				$query -> limit(Config::get('maxrows_rankinglist'));
				$old = $query -> execute() -> as_array();
			}

			// 過去との比較
			$prev = array();
			foreach ($old as $key => $value) {
				$prev[$value['song_id']] = $key;
			}
			foreach ($json as $key => $value) {
				$rank_now = $key;
				$rank_prev = isset($prev[$value['song_id']]) ? $prev[$value['song_id']] : 9999;
				if ($rank_now > $rank_prev) {
					// 上がった
					$json[$key]['transition'] = 'DOWN';
				} else if ($rank_now < $rank_prev) {
					// 下がった
					$json[$key]['transition'] = 'UP';
				} else {
					// 変わらず
					$json[$key]['transition'] = 'SAME';
				}
			}

			// キャッシュ保存
			$json = json_encode($json);
			Cache::set($ckey, $json, Config::get('cache_ranking_expiration'));
		}

		return $json;
	}

	/**
	 * バージョン別の制御
	 *
	 * @access  public
	 * @return  Response
	 */
	public function appver() {
		// バージョンの確認
		$post = Input::post();
		$ver = isset($post['_ver']) ? $post['_ver'] :'default';
		$get = Input::get();
		$ver = isset($get['_ver']) ? $get['_ver'] : $ver;
		
		return $ver;
	}

	/**
	 * バージョン別の制御
	 *
	 * @access  public
	 * @return  Response
	 */
	public function appver_more($min) {
		$e = explode('.', $this->appver() . '.0.0.0');
		$now = $e[0] * 100000 + $e[1] * 1000 + $e[2];
		
		$e = explode('.', $min . '.0.0.0');
		$min = $e[0] * 100000 + $e[1] * 1000 + $e[2];

		return $min <= $now;
	}

	/**
	 * アラートログ
	 *
	 * @access  public
	 * @return  Response
	 */
	public function alert($msg = "", $level = "warning") {
		$msg .= PHP_EOL;
		$msg .= Input::uri() . PHP_EOL;
		$msg .= Input::real_ip() . PHP_EOL;
		$msg .= Input::user_agent() . PHP_EOL;
		$msg .= print_r(Input::get(), true);
		$msg .= print_r(Input::post(), true);
		if ($level == "warning") {
			Log::warning($msg);
		} else {
			Log::debug($msg);
		}
	}

	/**
	 * アクセスログ
	 * Fluent経由でTreasureDataに書き出す
	 *
	 * @access  public
	 * @return  Response
	 */
	public function accesslog($json) {
		$td = Config::get('log_api');
		if (!empty($td)) {
			Fluent\Autoloader::register();
			$logger = new FluentLogger('unix:///var/run/td-agent/td-agent.sock');
			$logger -> post($td, $json);
		}
	}

}
